package com.wyc.serviceorder.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.wyc.internalCommon.constant.*;
import com.wyc.internalCommon.exception.OrderException;
import com.wyc.internalCommon.request.CarDTO;
import com.wyc.internalCommon.request.DirectionDTO;
import com.wyc.internalCommon.request.OrderInfo;
import com.wyc.internalCommon.request.PriceRuleDTO;
import com.wyc.internalCommon.response.DriverOrderResponse;
import com.wyc.internalCommon.response.TerminalResponse;
import com.wyc.internalCommon.response.TrSearchResponse;
import com.wyc.internalCommon.result.ApiResult;
import com.wyc.internalCommon.util.RedisKeyUtils;
import com.wyc.serviceorder.dto.OrderDTO;
import com.wyc.serviceorder.feign.ServiceDriverUserFeignClient;
import com.wyc.serviceorder.feign.ServiceMapFeignClient;
import com.wyc.serviceorder.feign.ServicePriceFeignClient;
import com.wyc.serviceorder.feign.ServiceSsePushFeignClient;
import com.wyc.serviceorder.mapper.OrderMapper;
import com.wyc.serviceorder.service.OrderService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import net.sf.json.JSONObject;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.temporal.ChronoUnit;
import java.util.List;
import java.util.concurrent.TimeUnit;


/**
 * <p>
 * 订单表 服务实现类
 * </p>
 *
 * @author li
 * @since 2023-04-02
 */
@Slf4j
@Service
public class OrderServiceImpl extends ServiceImpl<OrderMapper, OrderDTO> implements OrderService {

    @Autowired
    private OrderMapper orderMapper;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private ServicePriceFeignClient servicePriceFeignClient;

    @Autowired
    private ServiceDriverUserFeignClient serviceDriverUserFeignClient;

    @Autowired
    private ServiceMapFeignClient serviceMapFeignClient;

    @Autowired
    private RedissonClient redissonClient;

    @Autowired
    private ServiceSsePushFeignClient ssePushFeignClient;

    /**
     * 创建订单
     * @param orderInfo
     * @return
     */
    @Transactional
    @Override
    public ApiResult add(OrderInfo orderInfo) {

        String address = orderInfo.getAddress();
        String vehicleType = orderInfo.getVehicleType();
        String fareType = orderInfo.getFareType();
        Integer fareVersion = orderInfo.getFareVersion();
        // 查询当前城市是否有司机
        ApiResult<Boolean> driverUserResult = serviceDriverUserFeignClient.queryDriverByCityCode(address);

        if (!driverUserResult.getData()){
            return ApiResult.error(CommonStatusEnum.CITY_DRIVER_EMPTY.getCode(),CommonStatusEnum.CITY_DRIVER_EMPTY.getMsg());
        }

        // 计算运价版本是否是最新，不是返回error重新下单
        boolean isNew = isNew(address,vehicleType,fareType,fareVersion);
        if (!isNew){
            return ApiResult.error(CommonStatusEnum.PRICE_RULE_NOT_NEW.getCode(),CommonStatusEnum.PRICE_RULE_NOT_NEW.getMsg());
        }

        // 下单的设备是否是黑名单
        // 设备唯一码
//        String deviceCode = orderInfo.getDeviceCode();
//        if (deviceIsBlack(deviceCode))
//            return ApiResult.error(CommonStatusEnum.DEVICE_IS_BLACK.getCode(), CommonStatusEnum.DEVICE_IS_BLACK.getMsg());


        // 判断是否有正在进行的订单
        Long passengerId = orderInfo.getPassengerId();
        Integer count = isOrderGoingon(passengerId);

        if (count > 0){
            return ApiResult.error(CommonStatusEnum.ORDER_GOING_ON.getCode(),CommonStatusEnum.ORDER_GOING_ON.getMsg());
        }

        // 添加订单
        OrderDTO requestOrderDTO = new OrderDTO();
        BeanUtils.copyProperties(orderInfo,requestOrderDTO);
        requestOrderDTO.setOrderStatus(OrderConstant.ORDER_STATUS_START);
        orderMapper.insert(requestOrderDTO);

        // 订单派送
        dispatchRealTimeOrder(requestOrderDTO);

        return ApiResult.success();
    }

    /**
     * 司机出发去接乘客
     * @param orderInfo
     * @return
     */
    @Override
    public ApiResult toPickUpPassenger(OrderInfo orderInfo) {
        OrderDTO orderDTO = new OrderDTO();
        BeanUtils.copyProperties(orderInfo,orderDTO);
        orderDTO.setToPickUpPassengerTime(LocalDateTime.now());
        orderDTO.setOrderStatus(OrderConstant.ORDER_STATUS_DRIVER_PICK_PASSENGER);
        orderMapper.updateById(orderDTO);

        return ApiResult.success();
    }

    /**
     * 司机到达上车地点
     * @param orderId
     * @return
     */
    @Override
    public ApiResult driverArrivedDeparture(Long orderId) {
        OrderDTO orderDTO = new OrderDTO();
        orderDTO.setId(orderId);
        orderDTO.setDriverArrivedDepartureTime(LocalDateTime.now());
        orderDTO.setOrderStatus(OrderConstant.ORDER_STATUS_DRIVER_TO_START_POINT);
        orderMapper.updateById(orderDTO);
        return ApiResult.success();
    }

    /**
     * 司机接到乘客
     * @param orderInfo
     * @return
     */
    @Override
    public ApiResult pickUpPassenger(OrderInfo orderInfo) {
        OrderDTO orderDTO = new OrderDTO();
        BeanUtils.copyProperties(orderInfo,orderDTO);
        orderDTO.setPickUpPassengerTime(LocalDateTime.now());
        orderDTO.setOrderStatus(OrderConstant.ORDER_STATUS_DRIVE_RUNNING);
        orderMapper.updateById(orderDTO);
        return ApiResult.success();
    }

    /**
     * 乘客下车，行程结束
     * @param orderInfo
     * @return
     */
    @Transactional
    @Override
    public ApiResult passengerGetoff(OrderInfo orderInfo) {
        Long id = orderInfo.getId();
        OrderDTO orderDTO = orderMapper.selectById(id);
        String passengerGetoffLongitude = orderInfo.getPassengerGetoffLongitude();
        String passengerGetoffLatitude = orderInfo.getPassengerGetoffLatitude();
        orderDTO.setPassengerGetoffLongitude(passengerGetoffLongitude);
        orderDTO.setPassengerGetoffLatitude(passengerGetoffLatitude);
        orderDTO.setPassengerGetoffTime(LocalDateTime.now());
        orderDTO.setOrderStatus(OrderConstant.ORDER_STATUS_NON_PAY);

        // 终端id tid
        ApiResult<CarDTO> carById = serviceDriverUserFeignClient.getCarById(orderDTO.getCarId());
        String tid = carById.getData().getTid();
        // 乘客上车时间
        LocalDateTime pickUpPassengerTime = orderDTO.getPickUpPassengerTime();
        // 乘客下车时间
        LocalDateTime passengerGetoffTime = orderDTO.getPassengerGetoffTime();
        ApiResult<TrSearchResponse> trsearch = serviceMapFeignClient.trsearch(tid, pickUpPassengerTime.toInstant(ZoneOffset.of("+8")).toEpochMilli(), passengerGetoffTime.toInstant(ZoneOffset.of("+8")).toEpochMilli());

        // 载客里程（米）
        Long driveMile = trsearch.getData().getDriveMile();
        orderDTO.setDriveMile(driveMile);
        // 载客时间（分）
        Long driveTime = trsearch.getData().getDriveTime();
        orderDTO.setDriveTime(driveTime);

        // 计算价格
        String cityCode = orderDTO.getAddress();
        String vehicleType = orderDTO.getVehicleType();
        DirectionDTO directionDTO = new DirectionDTO();
        directionDTO.setDistance(driveMile);
        directionDTO.setDuration((driveTime*60));
        directionDTO.setCityCode(cityCode);
        directionDTO.setVehicleType(vehicleType);
        ApiResult priceResult = servicePriceFeignClient.calulatePrice(directionDTO);
        String price = String.valueOf(priceResult.getData());

        orderDTO.setPrice(new BigDecimal(price));
        orderMapper.updateById(orderDTO);
        return ApiResult.success();
    }

    /**
     * 支付完成
     * @param orderId
     */
    @Override
    public void pay(Long orderId) {
        OrderDTO orderDTO = new OrderDTO();
        orderDTO.setId(orderId);
        orderDTO.setOrderStatus(OrderConstant.ORDER_STATUS_PAY_FINISH);
        orderMapper.updateById(orderDTO);
    }


    /**
     * 取消订单
     * @param orderId
     * @param identity 取消人的身份标识（1.乘客 2.司机3.平台）
     * @return
     */
    @Override
    public ApiResult cancelOrder(Long orderId, String identity) {
        OrderDTO orderDTO = orderMapper.selectById(orderId);
        LocalDateTime receiveOrderTime = orderDTO.getReceiveOrderTime();
        Integer orderStatus = orderDTO.getOrderStatus();

        LocalDateTime cancelTime = LocalDateTime.now();
        Integer cancelTypeCode = null;

        int cancelType = 1;

        // 乘客取消订单
        if (IdentityContant.PASSENGER_IDENTITY.equals(identity)){
            switch (orderStatus){
                case OrderConstant.ORDER_STATUS_START:
                    cancelTypeCode = CancelOrderConstant.CANCEL_PASSENGER_BRING;
                    break;
                case OrderConstant.ORDER_STATUS_DRIVER_ACCEPT:
                case OrderConstant.ORDER_STATUS_DRIVER_PICK_PASSENGER:
                case OrderConstant.ORDER_STATUS_DRIVER_TO_START_POINT:
                    long between = ChronoUnit.MINUTES.between(receiveOrderTime, cancelTime);
                    if (between > 1){
                        cancelTypeCode = CancelOrderConstant.CANCEL_PASSENGER_VIOLATE;
                    }else {
                        cancelTypeCode = CancelOrderConstant.CANCEL_PASSENGER_BRING;
                    }
                    break;
                default:
                    cancelType = 0;
                    break;
            }
        }

        // 身份是司机，取消订单
        if (IdentityContant.DRIVER_IDENTITY.equals(identity)){
            switch (orderStatus){
                case OrderConstant.ORDER_STATUS_DRIVER_ACCEPT:
                case OrderConstant.ORDER_STATUS_DRIVER_PICK_PASSENGER:
                case OrderConstant.ORDER_STATUS_DRIVER_TO_START_POINT:
                    long between = ChronoUnit.MINUTES.between(receiveOrderTime, cancelTime);
                    if (between > 1){
                        cancelTypeCode = CancelOrderConstant.CANCEL_DRIVER_VIOLATE;
                    }else {
                        cancelTypeCode = CancelOrderConstant.CANCEL_DRIVER_BRING;
                    }
                    break;
                default:
                    cancelType = 0;
                    break;
            }
        }

        if (cancelType == 0){
            return ApiResult.error(CommonStatusEnum.ORDER_CANCEL_FILE.getCode(),CommonStatusEnum.ORDER_CANCEL_FILE.getMsg());
        }

        // 平台取消订单
        if (IdentityContant.COMPANY_IDENTITY.equals(identity)){
            cancelTypeCode = CancelOrderConstant.CANCEL_COMPANY;
        }

        orderDTO.setCancelTime(cancelTime);
        orderDTO.setCancelOperator(Integer.valueOf(identity));
        orderDTO.setCancelTypeCode(cancelTypeCode);
        orderDTO.setOrderStatus(OrderConstant.ORDER_STATUS_STOP);
        orderMapper.updateById(orderDTO);
        return ApiResult.success();
    }

    /**
     * 实时订单派送
     * @param orderDTO
     */
    public OrderDTO dispatchRealTimeOrder(OrderDTO orderDTO){
        // 预计出发地点经度
        String depLongitude = orderDTO.getDepLongitude();
        // 预计出发地点纬度
        String depLatitude = orderDTO.getDepLatitude();
        // 搜索半径，初始2000m
        int radius = 2000;
        String center = depLatitude+","+depLongitude;

        for (int i = 1; i <= 6; i++){
            orderDTO = aroundTerminal(center, radius, orderDTO);
            if (null == orderDTO.getDriverId()){
                log.info("第"+i+"轮未寻找到车辆");
                if (i == 6){
                    orderDTO.setOrderStatus(OrderConstant.ORDER_INVALID);
                    orderMapper.updateById(orderDTO);
                    break;
                }
            }else {
                log.info("找到了车辆");
                break;
            }
            try {
                Thread.sleep(20000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }

        return orderDTO;

    }

    /**
     * 搜索周边终端
     */
    public OrderDTO aroundTerminal(String center,Integer radius,OrderDTO orderDTO){
        ApiResult<List<TerminalResponse>> listApiResult = serviceMapFeignClient.aroundSearch(center, radius);
        List<TerminalResponse> list = listApiResult.getData();
        if (list.size() == 0){
            // 未搜索到终端，扩大范围
            log.info(radius+"米内，未找到车辆");
            if (radius == 5000){
                log.info("此轮派单没有找到车");
                return null;
            }
            radius = radius + 1000;
            aroundTerminal(center,radius,orderDTO);
        }

        // 搜索到终端后
        // 解析终端
        for (TerminalResponse terminalResponse : list) {
            Long carId = terminalResponse.getCarId();
            // 查询车辆信息
            ApiResult<CarDTO> carResult = serviceDriverUserFeignClient.getCarById(carId);
            CarDTO car = carResult.getData();
            if (car.getVehicleType().trim().equals(orderDTO.getVehicleType().trim())){
                log.info("车型不匹配");
                continue;
            }

            // 司机是否是出车状态，是否有正在进行的订单。
            log.info("车辆id:"+carId+"是否可出车...");
            ApiResult<DriverOrderResponse> availableDriver = serviceDriverUserFeignClient.getAvailableDriverByCarId(carId);
            if (availableDriver.getCode() == CommonStatusEnum.CAR_NOT_BIND_DRICER.getCode() ||
                    availableDriver.getCode() == CommonStatusEnum.AVAILABLE_DRIVER_EMPTY.getCode() ){
                // 没有可用的司机
                log.info("车辆id:"+carId+"车辆没有对应的司机");
                continue;
            }else {
                log.info("车辆id:"+carId+"找到了对应的司机");
                DriverOrderResponse driverData = availableDriver.getData();
                // 并发问题处理，多个订单匹配到了同一个司机
                // 使用sync在单机情况下可以，如果集群，多服务时还是会多个订单匹配到了同一个司机
                // 基于redisson实现分布式锁 redis方法setnx不存在设置成功
                // 锁司机id小技巧,字符串.intern()，原理最终指向都变成了字符串常量

//    同步锁            synchronized ((driverData.getDriverId()+"").intern()){
                // redisson分布式锁
                String lockKey = (driverData.getDriverId()+"").intern();
                RLock lock = redissonClient.getLock(lockKey);
                lock.lock();
                // 查询司机是否有正在进行的订单
                if (isOrderGoingonByDriverUser(driverData.getDriverId()) > 0){
                    // 有正在进行的订单
                    log.info(driverData.getDriverId()+"司机的订单正在进行中...");
                    lock.unlock();
                    continue;
                }
                log.info("找到了可接单的司机......");
                // 订单匹配司机  司机id,司机手机号,车辆id,接单时车辆经纬度,接单时间,驾驶证号,车牌号,订单状态
                Long driverId = driverData.getDriverId();
                String driverPhone = driverData.getDriverPhone();
                orderDTO.setDriverId(driverId);
                orderDTO.setDriverPhone(driverPhone);
                orderDTO.setCarId(carId);
                orderDTO.setLicenseId(driverData.getLicenseId());
                String vehicleNo = car.getVehicleNo();
                orderDTO.setVehicleNo(vehicleNo);
                orderDTO.setReceiveOrderTime(LocalDateTime.now());
                orderDTO.setOrderStatus(OrderConstant.ORDER_STATUS_DRIVER_ACCEPT);
                String longitude = terminalResponse.getLongitude();
                String latitude = terminalResponse.getLatitude();
                orderDTO.setReceiveOrderCarLongitude(longitude);
                orderDTO.setReceiveOrderCarLatitude(latitude);
                orderMapper.updateById(orderDTO);

                // 消息推送，通知司机
                // 添加推送给司机的信息
                Long passengerId = orderDTO.getPassengerId();
                JSONObject jsonObject = new JSONObject();
                jsonObject.put("passengerId",passengerId);
                jsonObject.put("passengerPhone",orderDTO.getPassengerPhone());
                jsonObject.put("departure",orderDTO.getDeparture());
                jsonObject.put("depLongitude",orderDTO.getDepLongitude());
                jsonObject.put("depLatitude",orderDTO.getDepLatitude());

                jsonObject.put("destination",orderDTO.getDestination());
                jsonObject.put("destLongitude",orderDTO.getDestLongitude());
                jsonObject.put("destLatitude",orderDTO.getDestLatitude());
                ssePushFeignClient.push(String.valueOf(driverId), IdentityContant.DRIVER_IDENTITY,jsonObject.toString());

                // 添加推送给乘客的信息
                JSONObject passengerJSONObject = new JSONObject();
                passengerJSONObject.put("driverId",driverId);
                passengerJSONObject.put("driverPhone",driverPhone);

                passengerJSONObject.put("receiveOrderCarLongitude",longitude);
                passengerJSONObject.put("receiveOrderCarLatitude",latitude);
                passengerJSONObject.put("vehicleNo",vehicleNo);

                // 添加乘客需要看到的车辆信息
                passengerJSONObject.put("brand",car.getBrand());
                passengerJSONObject.put("model",car.getModel());
                passengerJSONObject.put("vehicleColor",car.getVehicleColor());

                ssePushFeignClient.push(String.valueOf(passengerId), IdentityContant.PASSENGER_IDENTITY,passengerJSONObject.toString());

                // 释放锁
                lock.unlock();
                // 派单成功，推出循环
                break;
// 同步锁结尾               }

            }
        }

        return orderDTO;
    }

    /**
     * 下单的设备是否是黑名单
     * @param deviceCode
     * @return
     */
    private boolean deviceIsBlack(String deviceCode) {
        String deviceKey = RedisKeyUtils.BLACK_DEVICE_CODE_PREFIX+ deviceCode;
        Boolean aBoolean = stringRedisTemplate.hasKey(deviceKey);
        if (aBoolean){
            // 存在key 重复下单
            String s = stringRedisTemplate.opsForValue().get(deviceKey);
            int i = Integer.parseInt(s);
            // 当前设备下单次数超过限制
            if (i > 2){
                return true;
            }else {
                stringRedisTemplate.opsForValue().increment(deviceKey);
            }
        }else {
            // 第一次下单
            stringRedisTemplate.opsForValue().set(deviceKey,"1",1L, TimeUnit.HOURS);
        }
        return false;
    }

    /**
     * 判断计价规则是否最新
     * @param address
     * @param vehicleType
     * @param fareType
     * @param fareVersion
     * @return
     */
    private boolean isNew(String address, String vehicleType, String fareType, Integer fareVersion) {
        PriceRuleDTO priceRuleDTO = new PriceRuleDTO();
        priceRuleDTO.setCityCode(address);
        priceRuleDTO.setVehicleType(vehicleType);
        priceRuleDTO.setFareType(fareType);
        priceRuleDTO.setFareVersion(fareVersion);
        ApiResult apiResult = servicePriceFeignClient.isNew(priceRuleDTO);
        JSONObject jsonObject = JSONObject.fromObject(apiResult.getData());
        boolean isNew = jsonObject.getBoolean("data");
        return isNew;
    }

    /**
     * 判断乘客是否有正在进行的订单
     * @param passengerId
     * @return
     */
    private Integer isOrderGoingon(Long passengerId) {
        QueryWrapper<OrderDTO> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("passenger_id", passengerId);
        queryWrapper.and(wrapper -> {
            wrapper.eq("order_status",OrderConstant.ORDER_STATUS_START)
                    .or().eq("order_status",OrderConstant.ORDER_STATUS_DRIVER_ACCEPT)
                    .or().eq("order_status",OrderConstant.ORDER_STATUS_DRIVER_PICK_PASSENGER)
                    .or().eq("order_status",OrderConstant.ORDER_STATUS_DRIVER_TO_START_POINT)
                    .or().eq("order_status",OrderConstant.ORDER_STATUS_DRIVE_RUNNING)
                    .or().eq("order_status",OrderConstant.ORDER_STATUS_NON_PAY)
                    .or().eq("order_status",OrderConstant.ORDER_STATUS_START_PAYEE);
        });
        Integer count = orderMapper.selectCount(queryWrapper);
        return count;
    }

    /**
     * 判断司机是否有正在进行的订单
     * @param driverId
     * @return
     */
    private Integer isOrderGoingonByDriverUser(Long driverId) {

        QueryWrapper<OrderDTO> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("driver_id", driverId);
        queryWrapper.and(wrapper -> {
            wrapper.eq("order_status",OrderConstant.ORDER_STATUS_DRIVER_ACCEPT)
                    .or().eq("order_status",OrderConstant.ORDER_STATUS_DRIVER_PICK_PASSENGER)
                    .or().eq("order_status",OrderConstant.ORDER_STATUS_DRIVER_TO_START_POINT)
                    .or().eq("order_status",OrderConstant.ORDER_STATUS_DRIVE_RUNNING);
        });
        Integer count = orderMapper.selectCount(queryWrapper);
        log.info("司机正在进行的订单的数量"+count);
        return count;

    }

}
